package com.xc58.highreactor.server.core.scanner;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;

import com.xc58.highreactor.exception.NoImplClassFoundException;
import com.xc58.highreactor.server.core.constants.Constants;
import com.xc58.highreactor.server.core.loader.IncrementClassLoader;
import com.xc58.highreactor.util.IOUtils;

@SuppressWarnings({"unchecked"})
public class ClassVisitor extends SimpleFileVisitor<Path> {
	public static final HashMap<String,String> name2location = new HashMap<String, String>();
	
	private static final IncrementClassLoader classloader = new IncrementClassLoader();
	
	
	public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
			throws IOException {
		ClassNode cv = new ClassNode();
		InputStream input = new FileInputStream(file.toFile());
		byte[] result = IOUtils.toByteArray(input);
		input = new ByteArrayInputStream(result);
		ClassReader cr = new ClassReader(input);
		cr.accept(cv, ClassReader.SKIP_CODE);
		initClassMapping(file, cv, result);
		input.close();
		return super.visitFile(file, attrs);
	}


	private void initClassMapping(Path file, ClassNode cv, byte[] result) {
		List<AnnotationNode> annotations = cv.visibleAnnotations;
		String interfaceName = cv.name.replaceAll("/", ".");
		//check is have annotations
		if(annotations != null){
			Iterator<AnnotationNode> it = annotations.iterator();
			while(it.hasNext()){
				AnnotationNode node = it.next();
				//check is our annotation
				if("Lcom/xc58/highreactor/annotation/RPCService;".equals(node.desc)){
					ArrayList<Type> values = (ArrayList<Type>) node.values.get(1);
					if(values.size() == 0){
						throw new NoImplClassFoundException("Service [%s] don't have impl Class");
					}else{
						//get impl classes
						ArrayList<String> classList = new ArrayList<String>();
						for (int i = 0; i < values.size(); i++) {
							String className = values.get(0).getClassName();
							classList.add(className);
						}
						Class<?> clazz;
						try {
							clazz = classloader.initClass(interfaceName,result);
						} catch (Exception e) {
							throw new RuntimeException(e);
						}
						Constants.addMapping(interfaceName, new ServiceMapping(interfaceName, clazz, classList));
					}
				}else if("Lcom/xc58/highreactor/annotation/RPCImpl;".equals(node.desc)){
					name2location.put(interfaceName, file.toString());
				}
			}
		}
	}
}
